package com.yc.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

// 切面类 = 切点(方法+注解属性) + 增强(方法)
@Component
@Aspect
public class MyAspect {

    // 前置增强
    @Before("execution(* com.yc..dao.*.*(..))")
    public void before(){
        System.out.println("前置拦截");
    }

//    long time;
    // Spring 会以接口注入方式,将连接点对象注入进来
    @Before("myPointcut()")
    public void before1(JoinPoint jp){
//        time = System.currentTimeMillis();
        System.out.println("------------ 前置拦截 begin -----------");
        // 参数
        System.out.println("Arrays.toString(jp.getArgs()) = " + Arrays.toString(jp.getArgs()));
        // 方法签名
        System.out.println("jp.getSignature() = " + jp.getSignature());
        // 目标对象
        System.out.println("jp.getTarget() = " + jp.getTarget());
        System.out.println("------------ 前置拦截 end -----------");
    }

    @After("myPointcut()")
    public void after(JoinPoint jp){
        System.out.println("------------ 后置拦截 "+jp.getSignature()+" -----------");
    }

    // 切点方法: 定义切点
    @Pointcut("execution(* com.yc..dao.UserDaoImpl.select*(..))")
    public void myPointcut(){}

    //  返回拦截  => 只有业务方法执行成功才拦截, 并且可以注入业务方法的返回值
    @AfterReturning(value = "myPointcut()",returning = "ret") // 引用切点
    public void afterReturning(JoinPoint jp, Object ret){
//        long time1 = System.currentTimeMillis();
//        long length = time1 - time;
        System.out.println("------------ 返回拦截 "+jp.getSignature()+" -----------");
        System.out.println("------------ 返回拦截 "+ret+" -----------");
    }

    // 异常链接  => 只有业务方法执行出现异常才拦截, 并且可以注入该异常
    @AfterThrowing(value = "myPointcut()", throwing = "e") // 引用切点
    public void afterThrowing(JoinPoint jp, Throwable e){
        System.out.println("------------ 异常拦截 "+jp.getSignature()+" -----------");
        System.out.println("------------ 异常拦截 "+e+" -----------");
    }

    // 环绕增强
    // 返回值: 是业务方法的返回值
    // 连接点对象负责执行业务方法, 必须手动执行
    @Around("execution(* com.yc..dao.UserDaoImpl.count(..))")
    public Object around (ProceedingJoinPoint pjp) throws Throwable {
        try {
            System.out.println("环绕增强=>前置增强");
            long time = System.currentTimeMillis();
            // 执行业务方法
            Object ret = pjp.proceed();
            time = System.currentTimeMillis() - time;
            System.out.println(pjp + " 业务方法执行了 " + time +" 毫秒");
            System.out.println("环绕增强=>返回增强");
            return ret;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕增强=>异常增强");
            throw throwable;
        } finally {
            System.out.println("环绕增强=>后置增强");
        }
    }

    /**
     *
     *  try{
     *                前置增强
     *      业务方法
     *                返回增强
     *  } catch {
     *                异常增强
     *  } finally {
     *                后置增强
     *  }
     *
     *  性能监控:
     *
     */

    // 前置增强
    @Before("execution(* com.yc..biz.UserBiz.*(..))")
    public void before1(){
        System.out.println("前置拦截");
    }

}
